package com.ailesson.video.util;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import com.ailesson.common.except.GlobalErrorCodeConstants;
import com.ailesson.common.except.ServiceException;
import com.ailesson.common.util.date.DateUtils;
import com.ailesson.common.util.io.FileUtils;
import com.ailesson.video.config.AliOSSConfig;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.vod.model.v20170321.CreateUploadVideoRequest;
import com.aliyuncs.vod.model.v20170321.CreateUploadVideoResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class FileUploadUtil {

    @Autowired
    private AliOSSConfig aliOSSConfig;



    public String upload(MultipartFile file){
        String format = LocalDateTimeUtil.format(LocalDate.now(), DateUtils.FORMAT_YEAR_MONTH_DAY);
        String path = aliOSSConfig.getPrefix()+"/"+format+ "/"+FileUtils.changeName(file.getOriginalFilename());
        OSS oss = null;
        try {
            oss = aliOSSConfig.ossClient();
            PutObjectRequest putObjectRequest = new PutObjectRequest(aliOSSConfig.getBucket(), path, file.getInputStream());
            // 设置该属性可以返回response。如果不设置，则返回的response为空。
            putObjectRequest.setProcess("true");
            // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            metadata.setObjectAcl(CannedAccessControlList.PublicRead);
            putObjectRequest.setMetadata(metadata);
            PutObjectResult putObject = oss.putObject(putObjectRequest);
            if (putObject == null || !putObject.getResponse().isSuccessful()){
                log.error("上传文件错误:{}",putObject.getResponse().getErrorResponseAsString());
                throw new RuntimeException("上传文件错误");
            }
            return putObject.getResponse().getUri();
        } catch (ClientException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            if (oss != null) {
               // oss.shutdown();
            }
        }
    }

    public Object videoUpload(MultipartFile file, String chapterName) {
        IAcsClient acsClient = aliOSSConfig.acsClient();
        CreateUploadVideoRequest request = new CreateUploadVideoRequest();
        request.setTitle(file.getOriginalFilename());
        /*String newFileName = "video_"+FileUtils.changeName(file.getOriginalFilename());
        String format = LocalDateTimeUtil.format(LocalDate.now(), DateUtils.FORMAT_YEAR_MONTH_DAY);
        String path = aliOSSConfig.getPrefix()+"/"+format+ "/"+newFileName;*/
        String filePath = aliOSSConfig.getVideoPartPath()+file.getOriginalFilename();
        request.setFileName(file.getOriginalFilename());
        request.setTags(chapterName);
        try {
            BufferedInputStream inputStream= new BufferedInputStream(file.getInputStream());
            FileUtil.writeFromStream(inputStream,filePath);

            CreateUploadVideoResponse createUploadVideoResponse = acsClient.getAcsResponse(request);
           /* System.out.print("VideoId = " + createUploadVideoResponse.getVideoId() + "\n");
            System.out.print("UploadAddress = " + createUploadVideoResponse.getUploadAddress() + "\n");
            System.out.print("UploadAuth = " + createUploadVideoResponse.getUploadAuth() + "\n");
            System.out.print("RequestId = " + createUploadVideoResponse.getRequestId() + "\n");*/
            // 执行成功会返回VideoId、UploadAddress和UploadAuth
            String videoId = createUploadVideoResponse.getVideoId();
            JSONObject uploadAuth = JSONObject.parseObject(new String(Base64Utils.decodeFromString(createUploadVideoResponse.getUploadAuth()), StandardCharsets.UTF_8));
            JSONObject uploadAddress = JSONObject.parseObject(new String(Base64Utils.decodeFromString(createUploadVideoResponse.getUploadAddress()), StandardCharsets.UTF_8));
            String endpoint = uploadAddress.getString("Endpoint");
            String accessKeyId = uploadAuth.getString("AccessKeyId");
            String accessKeySecret = uploadAuth.getString("AccessKeySecret");
            String securityToken = uploadAuth.getString("SecurityToken");
            OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret, securityToken);
            uploadEnableCheckPointFile(ossClient,uploadAddress,filePath);
            Map<String,Object> upVideoMap = new HashMap<>();
            upVideoMap.put("videoSourceId",videoId);
            upVideoMap.put("videoSize",file.getSize()/1024);
            //删除缓存文件
            File localFile = new File(filePath);
            localFile.deleteOnExit();
            return upVideoMap;
        } catch (ClientException e) {
            log.error("视频上传-获取客户端报错：{}",e);
            throw new ServiceException(GlobalErrorCodeConstants.FILE_UPVIDEO_ERROR);
        } catch (IOException e) {
            log.error("视频上传-io报错：{}",e);
            throw new ServiceException(GlobalErrorCodeConstants.FILE_UPVIDEO_ERROR);
        } catch (Throwable t){
            log.error("视频上传-断点续传报错：{}",t);
            throw new ServiceException(GlobalErrorCodeConstants.FILE_UPVIDEO_ERROR);
        }


    }

    /**
     *  断点续传上传
     * @throws Exception
     */
    public static void uploadEnableCheckPointFile(OSSClient ossClient, JSONObject uploadAddress, String localFile) throws Throwable {
        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");

        try {
            UploadFileRequest uploadFileRequest = getUploadFileRequest(localFile, bucketName, objectName);
            // 记录本地分片上传结果的文件。上传过程中的进度信息会保存在该文件中，如果某一分片上传失败，再次上传时会根据文件中记录的点继续上传。上传完成后，该文件会被删除。
            // 如果未设置该值，默认与待上传的本地文件同路径，名称为${uploadFile}.ucp。
            //uploadFileRequest.setCheckpointFile("yourCheckpointFile");
            // 断点续传上传。
            ossClient.uploadFile(uploadFileRequest);

        } catch (Throwable oe) {
           throw oe;
        } finally {
            // 关闭OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    private static UploadFileRequest getUploadFileRequest(String localFile, String bucketName, String objectName) {
        UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, objectName);
        // 通过UploadFileRequest设置单个参数。
        // 填写本地文件的完整路径，例如D:\\localpath\\examplefile.mp4。如果未指定本地路径，则默认从示例程序所属项目对应本地路径中上传文件。
        uploadFileRequest.setUploadFile(localFile);
        // 指定上传并发线程数，默认值为1。
        uploadFileRequest.setTaskNum(5);
        // 指定上传的分片大小，单位为字节，取值范围为100 KB~5 GB。默认值为100 KB。
        uploadFileRequest.setPartSize(1 * 1024 * 1024);
        // 开启断点续传，默认关闭。
        uploadFileRequest.setEnableCheckpoint(true);
        return uploadFileRequest;
    }


}
